/**
 * 
 */
package jm.lib.common.db;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import jm.lib.util.StrUtil;
import jm.lib.util.ValidUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * @author JimingLiu
 * 
 */
public enum ConditionOperation {
	EQUALS("=", "equal")
	, GE(">=", "greater and equal")
	, LE("<=", "less and equal")
	, GT(">", "greater than")
	, LT("<", "less than")
	, NE("<>", "not equal")
	, IN("IN", "value in")
	, BETWEEN("BETWEEN", "between")
	, LIKE("LIKE", "like")
	;

	
	
	private static final Log logger = LogFactory
	.getLog(ConditionOperation.class);

	private final String name;

	private final String description;

	ConditionOperation(String name, String desc) {
		this.name = name;
		this.description = desc;
	}

	/**
	 * compose the condition string for sql query
	 * 
	 * @param key
	 * @param value
	 * @param withQuotation
	 * @return
	 */
	public String compose(String key, String value, boolean withQuotation) {
		StringBuilder sb = new StringBuilder();
		sb.append(key).append(' ');
		composeValue(key, value, withQuotation, sb);
		return sb.toString();
	}

	private void composeValue(String key, String value, boolean withQuotation, StringBuilder sb) {
		switch (this) {
		case EQUALS:
			if (ValidUtil.isValidDateString(value)) {
				sb.append(">= '");
				sb.append(value).append(" 0:0:0' AND ");
				sb.append(key).append(" <= '").append(value);
				sb.append(' ').append("23:59:59'");
			} else {
				sb.append("= '").append(value).append('\'');
			}
			break;
		case BETWEEN: {
			String values = value.replace(",", withQuotation?"' AND '":" AND ");
			sb.append("BETWEEN ");
			if(withQuotation) sb.append("'");
			sb.append(values);
			if(withQuotation) sb.append("'");
		}
			break;
		case IN: {
			String values = withQuotation?value.replace(",", "','"):value;
			sb.append("IN (");
			if(withQuotation) sb.append("'");
			sb.append(values);
			if(withQuotation) sb.append("'");
			sb.append(")");
		}
			break;
		case LIKE: {
			sb.append("LIKE '");
			if(-1 == value.indexOf('%')) {
				sb.append('%').append(value).append('%');
			} else {
				sb.append(value);
			}
			sb.append('\'');
		}
			break;
		default:
			sb.append(this.getName()).append(" ");
			if(withQuotation) sb.append("'");
			sb.append(value);
			if(withQuotation) sb.append("'");
			break;

		}
	}

	public boolean isValidParam(String value) {
		if(StrUtil.isEmpty(value)) {
			if(logger.isWarnEnabled()) {
				logger.warn("Value should not be empty.");
			}
			return false;
		}
		// Reduce the risk of SQL injection
		if(value.length() > 16) {
			if(logger.isWarnEnabled()) {
				logger.warn("Value [" + value + "]'s length should less than 16.");
			}
			return false;
		}
		// Forbidden ', ", \ in the query string
		if(-1 != value.indexOf('\'') 
				|| -1 != value.indexOf('"')
				|| -1 != value.indexOf('\\')
				) {
			if(logger.isWarnEnabled()) {
				logger.warn("Value [" + value + "]'s should not contain any of ['\"\\].");
			}
			return false;
		}
			
		// using BETWEEN, there must have one and only one comma
		if (BETWEEN == this) {
			int index = value.indexOf(',');
			if (-1 == index || 0 == index || value.length()-1 == index) {
				if(logger.isWarnEnabled()) {
					logger.warn("Value [" + value + "]'s should in exactly format [aaa,bbb].");
				}
				return false;
			}
			if (-1 != value.indexOf(',', index + 1)) {
				if(logger.isWarnEnabled()) {
					logger.warn("Value [" + value + "]'s should in exactly format [aaa,bbb].");
				}
				return false;
			}
		}
		return true;
	}

	public String getDescription() {
		return description;
	}

	public String getName() {
		return name;
	}

	private static Map<String, ConditionOperation> _operationMaps = new HashMap<String, ConditionOperation>();

	private static Map<String, String> _operations = new HashMap<String, String>();
	static {
		ConditionOperation[] cos = ConditionOperation.values();
		for (int i = 0; i < cos.length; i++) {
			_operationMaps.put(cos[i].getName(), cos[i]);
			_operations.put(cos[i].getName(), cos[i].getDescription());
		}
		_operations = Collections.unmodifiableMap(_operations);
	}

	public static ConditionOperation getInstance(String name) {
		String name1 = name.toUpperCase();
		return _operationMaps.get(name1);
	}

	public static Map<String, String> getForSelect() {
		return _operations;
	}

}
